<?php
// $Id: Db.php 27 2010-11-16 12:49:54Z mjohnson60@gmail.com $
//
// $Author: mjohnson60@gmail.com $
// $Date: 2010-11-10 22:31:26 -0500 (Wed, 10 Nov 2010) $
//
// $Rev: 23 $
//


/**
 * @var string Enter description here ...
 */
define('DB_QUERY_REPLACE_TOKEN', '/(%d|%s|%%|%f|%b|%n)/');

/**
 * Enter description here ...
 * 
 * @author Mark Phillip Johnson
 * @version 1.0
 * @copyright Copyright (c) 2010, markjohnson
 * @license http://www.gnu.org/licenses/gpl.html The GNU General Public License v3.0
 *
 */
class Db extends Object
{
	/**
	 * @var unknown_type Enter description here ...
	 */
	private static $instance = NULL;
	
	/**
	 * @var unknown_type Enter description here ...
	 */
	var $db_url = 'mysql://username:password@localhost/databasename';
	
	/**
	 * @var unknown_type Enter description here ...
	 */
	var $active_db = NULL;
	
	/**
	 * @var unknown_type Enter description here ...
	 */
	var $schema = NULL;
	
	/**
	 * @var unknown_type Enter description here ...
	 */
	var $conn = NULL;

	/**
	 * Enter description here ...
	 * 
	 */
	public function Db()
	{
		$this->db_url = Config::getInstance()->db_url;
		
		self::$instance = & $this;
	}

	/**
	 * getInstance()
	 * 
	 * Enter description here ...
	 * 
	 * @return Ambiguous
	 */
	public static function &getInstance()
	{
		if (!isset(self::$instance))
		{
			new Db();
		}
		
		return self::$instance;
	}

	/**
	 * schema()
	 * 
	 * Enter description here ...
	 * 
	 */
	public static function schema()
	{
		$_this = & Db::getInstance();
		return $_this->schema;
	}

	/**
	 * Enter description here ...
	 * @param unknown_type $vars
	 */
	public static function init()
	{
		$db = & Db::getInstance();
		$db->_init();
	}

	/**
	 * Enter description here ...
	 * @param unknown_type $vars
	 */
	public function _init()
	{
		if (empty($this->db_url) && (!defined('CONFIGURATION_MODE') || CONFIGURATION_MODE != 'INSTALL'))
		{
			include_once ('./includes/install.inc');
			install_launch('install.php');
		}
		
		if (!isset($this->conn))
		{
			$this->conn = $this->connect($this->db_url);
		}
		
		$this->active_db = $this->conn;
	}

	/**
	 * connect()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $url
	 * @return unknown|boolean
	 */
	private function connect($url)
	{
		$url = parse_url($url);
		
		$url['user'] = urldecode($url['user']);
		$url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : '';
		$url['host'] = urldecode($url['host']);
		$url['path'] = urldecode($url['path']);
		
		if (!isset($url['port']))
		{
			$url['host'] = $url['host'] . ':' . $url['port'];
		}
		
		if (function_exists('mysql_connect'))
		{
			$connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2);
			$use_db = mysql_select_db(substr($url['path'], 1));
			
			if (!$connection || !$use_db)
			{
				if (defined('CONFIGURATION_MODE') && CONFIGURATION_MODE == 'INSTALL')
				{
					$this->schema = ($use_db === FALSE) ? substr($url['path'], 1) : FALSE;
					
					return $connection;
				}
				
				return FALSE;
			}
			else 
			{
				$this->schema = ($use_db !== FALSE) ? substr($url['path'], 1) : FALSE;
			}
			
			mysql_query('SET NAMES "utf8"', $connection);
			return $connection;
		}
		else
		{
			Athena::setMessage('You must enable the php_mysql module for this installation to work.');
			return FALSE;
		}
	}

	/**
	 * escapeString()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $text
	 * @return string
	 */
	public static function escapeString($text)
	{
		$_this = & Db::getInstance();
		return mysql_real_escape_string($text, $_this->active_db);
	}

	/**
	 * encodeBlob()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $data
	 * @return string
	 */
	public static function encodeBlob($data)
	{
		$_this = & Db::getInstance();
		return "'" . mysql_real_escape_string($data, $_this->active_db) . "'";
	}

	/**
	 * query()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $sql
	 * @param unknown_type $args
	 * @return Ambigous <resource, boolean>
	 */
	public static function query()
	{
		$args = func_get_args();
		$sql = array_shift($args);
		
		if (count($args) == 1 && is_array($args[0]))
		{
			$args = $args[0];
		}
		
		Db::_replaceTokenCallback($args, TRUE);
		$query = preg_replace_callback(DB_QUERY_REPLACE_TOKEN, 'Db::_replaceTokenCallback', $sql);
		
		return Db::_query($query);
	}

	/**
	 * _query()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $query
	 * @return resource|boolean
	 */
	private static function _query($query)
	{
		$_this = & Db::getInstance();
		$result = mysql_query($query, $_this->active_db);
		
		if (!mysql_errno($_this->active_db))
		{
			return $result;
		}
		else
		{
			trigger_error(mysql_error($_this->active_db) . "\nQUERY: " . $query, E_USER_WARNING);
			return FALSE;
		}
	}

	/**
	 * _replaceTokenCallback()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $match
	 * @param unknown_type $declare
	 * @return void|number|string
	 */
	public static function _replaceTokenCallback($match, $declare = FALSE)
	{
		static $args = NULL;
		
		if ($declare)
		{
			$args = $match;
			return;
		}
		
		switch ($match[1])
		{
			case '%d':
				// We must use type casting to int to convert FALSE/NULL/(TRUE?)
				$value = array_shift($args);
				$value = (int) $value;
				
				// We don't need db_escape_string as numbers are db-safe.
				return $value;
			case '%s':
				return Db::escapeString(array_shift($args));
			case '%n':
				// Numeric values have arbitrary precision, so can't be treated as float.
				// is_numeric() allows hex values (0xFF), but they are not valid.
				$value = trim(array_shift($args));
				return is_numeric($value) && !preg_match('/x/i', $value) ? $value : '0';
			case '%%':
				return '%';
			case '%f':
				return (float) array_shift($args);
			case '%b': // binary data
				return Db::encodeBlob(array_shift($args));
		}
	}

	/**
	 * fetchArray()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $result
	 * @return multitype:
	 */
	public static function fetchArray($result)
	{
		return mysql_fetch_array($result, MYSQL_ASSOC);
	}

	/**
	 * result()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $result
	 * @return Ambigous <>|boolean
	 */
	public static function result($result)
	{
		if ($result && mysql_num_rows($result) > 0)
		{
			$array = mysql_fetch_row($result);
			return $array[0];
		}
		
		return false;
	}

	/**
	 * affectedRows()
	 * 
	 * Enter description here ...
	 * 
	 * @return number
	 */
	public static function affectedRows()
	{
		$_this = & Db::getInstance();
		return mysql_affected_rows($_this->active_db);
	}

	/**
	 * fetchObject()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $result
	 * @return object
	 */
	public static function fetchObject($result)
	{
		if ($result)
		{
			return mysql_fetch_object($result);
		}
	}

	/**
	 * isActive()
	 * 
	 * Enter description here ...
	 * 
	 * @return boolean
	 */
	public static function isActive()
	{
		$_this = & Db::getInstance();
		return !empty($_this->active_db);
	}

	/**
	 * dropTable()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $ret
	 * @param unknown_type $table
	 */
	public static function dropTable(&$ret, $table)
	{
		$result = Db::query('DROP TABLE ' . $table);
		$ret[] = ($result !== FALSE);
	}

	/**
	 * numRows()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $result
	 * @return number|boolean
	 */
	public static function numRows($result)
	{
		if ($result !== FALSE)
		{
			return mysql_num_rows($result);
		}
		
		return FALSE;
	}

	/**
	 * lastInsertId()
	 * 
	 * Enter description here ...
	 * 
	 * @param string $table
	 * @param string $field
	 * @return int
	 */
	public static function lastInsertId($table, $field)
	{
		return Db::result(Db::query('SELECT LAST_INSERT_ID()'));
	}
}
